//#include <fstream>
//#include <cstdint>
//#include "HairReader.h"
//#include "HairSimulator.h"
#include "HairCore.h"

namespace XRwy {
	using namespace std;

	HairReader::HairReader(const std::string file_path, float scale, HairReaderMirrorType mirror, int sample_freq): HairReader(file_path.c_str(), scale, mirror, sample_freq) {
	}

	HairReader::HairReader(const char *file_path, float scale, HairReaderMirrorType mirror, int sample_freq) {
		ifstream fin(file_path, ios::in | ios::binary);
		init(fin, scale, mirror, sample_freq);
	}

	HairReader::HairReader(std::istream &input_stream, float scale, HairReaderMirrorType mirror,
	                       int sample_freq) {
		init(input_stream, scale, mirror, sample_freq);
	}

	void HairReader::init(std::istream &input_stream, float scale, HairReaderMirrorType mirror, int sample_freq) {
		using namespace std;
		using namespace Eigen;

		READ_INT32(input_stream, total_particle_count);

		//read all the strand and initialize the strand count
		input_stream.seekg(total_particle_count * FLOAT_SIZE * 3 + INT32_SIZE, ios::beg);
		READ_INT32(input_stream, total_strand_count);
		int *strand_count = new int[total_strand_count];
		if (input_stream.tellg())
		for (int i = 0; i < total_strand_count; ++i)
			input_stream.read((char*)(&strand_count[i]), INT32_SIZE);

		//use the particle to initialize all the strand struct
		input_stream.seekg(INT32_SIZE, ios::beg);
		auto tmp_strands = vector<StrandStruct>(total_strand_count);
		for (int i = 0; i < total_strand_count; ++i) {
			auto &strand = tmp_strands[i];
			strand.reserve(strand_count[i]);

			for (int j = 0; j < strand_count[i]; ++j) {
				READ_SINGLE(input_stream, x);
				READ_SINGLE(input_stream, y);
				READ_SINGLE(input_stream, z);
				strand.emplace_back(
						x * scale * (mirror & MIRROR_X ? -1.0f : 1.0f),
						y * scale * (mirror & MIRROR_Y ? -1.0f : 1.0f),
						z * scale * (mirror & MIRROR_Z ? -1.0f : 1.0f));
			}
		}

		this->particle_count = 0;
		this->strand_count = 0;

		int selected_strand_count = max(1, total_strand_count / sample_freq);
		for (int i = 0; i < selected_strand_count; ++i) {
			this->strands.push_back(std::move(tmp_strands[i]));
			this->particle_count += this->strands.back().size();
			this->strand_count++;
		}

		delete[] strand_count;
	}

	HairReader HairReader::resample(const vector<size_t> &strand_ids) const {
		HairReader ret;

		for (const auto strand_id : strand_ids) {
			ret.strands.push_back(this->strands[strand_id]);

			ret.particle_count += ret.strands.back().size();
			ret.strand_count += 1;
		}

		return ret;
	}

	int HairReader::get_strand_count() const {
		return this->strand_count;
	}

	const HairReader::StrandArray &HairReader::get_strands() const {
		return this->strands;
	}

	int HairReader::get_particle_count() const {
		return particle_count;
	}
}
